#pragma once

#include <string>
#include <vector>
#include "Singleton.h"
#include "World.h"
#include "System.h"
#include "CameraNode.h"
#include "Entity.h"
#include "IAbility.h"
#include "IEffect.h"
#include "IBehavior.h"
#include "GraphicalComponent.h"
#include "SelectableComponent.h"
#include "CombatComponent.h"
#include "OrientationComponent.h"
#include "StatusComponent.h"
#include "RigidBodyComponent.h"
#include "EntityController.h"
#include "Avatar.h"
#include "Uniform.h"


class Game : public Singleton<Game>
{
public:
	void Initialize();
	void Deinitialize();
	void LoadDefaults();
	
	bool LoadTexture( std::string filename );
	
	bool CreateEntity( HashedString avatarName );
	bool CreateEntity( HashedString avatarName, Pointer<Entity>& entity );
	bool CreateEntity( HashedString avatarName, HashedString entityName );
	bool CreateEntity( HashedString avatarName, HashedString entityName, Pointer<Entity>& entity );
	void RetrieveEntity( HashedString entityName, Pointer<Entity>& entity );
	void RemoveEntity( HashedString entityName );
	void RemoveEntity( Entity* entity );
	void CreateController( HashedString entityName );
	void CreateController( HashedString entityName, Pointer<EntityController>& controller );
	void CreateController( Entity* entity, Pointer<EntityController>& controller );
	void RetrieveController( HashedString entityName, Pointer<EntityController>& controller );
	void RetrieveController( Pointer<Entity> entity, Pointer<EntityController>& controller );
	void RemoveController( HashedString entityName );
	void RemoveController( EntityController* controller );
	void RemoveController( Entity* entity );
	void CreateBehavior( HashedString type, EntityController* controller, Pointer<IBehavior>& behavior );
	void CreateAbility( HashedString type, Combat::Combatant* controller, Pointer<Combat::IAbility>& ability );
	void CreateEffect( HashedString type, Combat::Combatant* controller, Pointer<Combat::IEffect>& effect );
	void CreateUniformInstruction( HashedString type, Pointer<IUniformInstruction>& instruction ); 
	void SetEntitySelectable( HashedString entityName );
	void SetEntitySelectable( Pointer<Entity>& entity );
	void SetEntityUnselectable( HashedString entityName );
	void SetEntityUnselectable( Pointer<Entity>& entity );
	void SetEntityGroup( HashedString groupName, HashedString entityName );
	void SetEntityGroup( HashedString groupName, Entity* entity );
	void RemoveEntityFromAllGroups( HashedString entityName );
	void RemoveEntityFromAllGroups( Entity* entity );
	void AddEntityToPathingWorld( HashedString worldName, HashedString entityName );
	void AddEntityToPathingWorld( HashedString worldName, Pointer<Entity>& entity );
	void RemoveEntityFromPathingWorld( HashedString worldName, HashedString entityName );
	void AddEntityToSimulationWorld( HashedString worldName, HashedString entityName );
	void AddEntityToSimulationWorld( HashedString worldName, Pointer<Entity>& entity );
	void RemoveEntityFromSimulationWorld( HashedString worldName, HashedString entityName );
	void RemoveEntityFromSimulationWorld( HashedString worldName, Pointer<Entity>& entity );
	
	// Pathing
	void CreatePathingWorld( HashedString worldName );
	void CreatePathingWorld( HashedString worldName, Pointer<Pathing::World>& world );
	void RetrievePathingWorld( HashedString worldName, Pointer<Pathing::World>& world );
	
	// Simulations
	void CreateSimulationWorld( HashedString worldName );
	void CreateSimulationWorld( HashedString worldName, System*& world );
	void RetrieveSimulationWorld( HashedString worldName, System*& world );

	// SceneGraph
	void RememberNode( HashedString nodeName, Pointer<INode> node );
	void RetrieveNode( HashedString nodeName, Pointer<INode>& node );
	void ForgetNode( HashedString nodeName );

	// Utility
	bool RetrieveEntityGroup( HashedString groupName, std::list<Entity*>*& list );
	bool isEntityWithinGroup( HashedString groupName, Entity* entity );
	bool EntityExists( Entity* entity );
	bool LoadFromFile( std::string filename );
	bool SaveToFile( std::string filename );
	Entity* Select( int x, int y );
	Entity* Select( int x, int y, Maths::Vector3D& point );
	Pointer<CameraNode> RetrieveActiveCamera();
	void setActiveCamera( Pointer<CameraNode>& camera );
	void RegisterCamera( HashedString cameraName, Pointer<CameraNode>& cameraNode, bool setActive = false );

	void PauseTasks();
	void ResumeTasks();

	// Register functions 
	static bool RegisterBehavior( HashedString type, IBehavior::CreateFunction func)
	{
		m_behaviorCreators.insert(IBehavior::CreatorPair_t(type, func));
		return true;
	};
	static bool RegisterAbility(HashedString type, Combat::IAbility::CreateFunction func)
	{
		m_abilityCreators.insert(Combat::IAbility::CreatorPair_t(type, func));
		return true;
	};
	static bool RegisterEffect(HashedString type, Combat::IEffect::CreateFunction func)
	{
		m_effectCreators.insert(Combat::IEffect::CreatorPair_t(type, func));
		return true;
	};
	static bool RegisterUniformInstruction(HashedString type, IUniformInstruction::CreateFunction func)
	{
		m_uniformInstructions.insert(IUniformInstruction::CreatorPair_t(type, func));
		return true;
	};
	// System functions - avoid use
	Entity::Map_t& _getAllEntities();
	Pointer<System>& _getSelectables();
	std::vector<std::string>& _getTextureStrings();
	EntityController::Vector_t& _getControllers();
	Pathing::World::Map_t& _getPathingWorlds();
	Pointer<CameraNode>& _getActiveCamera();
	std::map<HashedString, std::vector<Entity*>>& _getAvatarEntityMap();
protected:
	Pointer<CameraNode> m_activeCamera;
	// Entity groups
	std::map<HashedString, std::list<Entity*>> m_entityGroups;
	// selectable system; used for raycast from pointer
	Pointer<System> m_selectables;
	// list of all controllers
	EntityController::Vector_t m_controllers;
	// list of textures
	std::vector<std::string> m_textureStrings;
	// list of all entities, by name
	Entity::Map_t m_entities;
	// Worlds - containers of obstacles, by name
	Pathing::World::Map_t m_pathingWorlds;
	// Avatar to Current Entities
	std::map<HashedString, std::vector<Entity*>> m_avatarEntityMap;
	// Node names
	INode::Map_t m_nodeNames;
	
	static IBehavior::CreatorMap_t m_behaviorCreators;
	static Combat::IAbility::CreatorMap_t m_abilityCreators;
	static Combat::IEffect::CreatorMap_t m_effectCreators;
	static IUniformInstruction::CreatorMap_t m_uniformInstructions;

	CombatComponent* CreateCombatComponent( Entity* entity, Avatar::Data::Vector_t& dataArray );
	StatusComponent* CreateStatusComponent( Entity* entity, Avatar::Data::Vector_t& dataArray );
	RigidBodyComponent* CreateRigidBodyComponent( Entity* entity, Avatar::Data::Vector_t& dataArray );
	SelectableComponent* CreateSelectableComponent( Entity* entity, Avatar::Data::Vector_t& dataArray );
	GraphicalComponent* CreateGraphicalComponent( Entity* entity, Avatar::Data::Vector_t& dataArray );
	OrientationComponent* CreateOrientationComponent( Entity* entity, Avatar::Data::Vector_t& dataArray );
	EntityController* CreateEntityController( Entity* entity, Avatar::Data::Vector_t& dataArray );
};